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

Tha*_*san 33 php laravel eloquent

我正在使用Laravel并且在Eloquent ORM中遇到一个小问题.我可以使用JOIN简单地使用SQL查询,但我似乎无法使用Eloquent进行操作!

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

表很简单..和一对一的关系.像有一个restaurant带桌子id,name,slug,等,并叫另一台restaurant_facilitiesid,restaurant_id,wifi,parking,等

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

对于多对多关系我也有同样的问题cuisines!我有restaurant同桌,cuisine桌子和restaurant_cuisine_connection桌子..

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

这有效.

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

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

任何想法或方法?我一直在努力奋斗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');
    }

}
Run Code Online (Sandbox Code Playgroud)

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

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

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

请帮忙.

谢谢.

Jef*_*Way 32

如果你必须从餐厅模型加载,我在这里做左连接没有任何问题.我可能会将它抽象为我的餐厅模型上的方法,如下所示:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,从您的路线:

Route::get('/', function()
{
  return Restaurant::withWifi()->get();
});
Run Code Online (Sandbox Code Playgroud)

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

(PS我会坚持使用单一形式的Facility.请注意如何hasOne('Facilities')正确读取?)


Thr*_*ead 20

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

/api/shared/{share_id}/subitem/{subitem_id}
Run Code Online (Sandbox Code Playgroud)

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

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

所以只想要那个有id的子项.现在我们可以通过执行以下操作来检查是否找到了它:

if ($shared->subitems->isEmpty())
Run Code Online (Sandbox Code Playgroud)

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

return $shared->subitems[0];
Run Code Online (Sandbox Code Playgroud)


Eri*_*ker 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();
Run Code Online (Sandbox Code Playgroud)

使用本地范围实现相同的目标:

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);
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

对于本地作用域,您不希望将它们定义为模型上的静态方法,因为这会创建查询构建器的新实例,并会阻止您链接方法.使用本地作用域将注入并返回查询构建器的当前实例,以便您可以根据需要链接任意数量的作用域:

Restaurant::hasWifi()->hasCuisine(6)->get();
Run Code Online (Sandbox Code Playgroud)

本地作用域使用scope方法名称中的前缀定义,并在方法名称中调用scope,如abover示例中所示.


Yau*_*hyk 10

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

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

Facilities::whereHas('restaurant', $with_wifi)
Run Code Online (Sandbox Code Playgroud)

干净整洁.