Laravel从错误的行中提取信息?说明?

时间:2017-12-15 22:36:31

标签: laravel

我正在使用laravel 5.5,我有这个代码

$building = Building::findOrFail($building_id);

$units = $building->units()
                ->whereHas('parkingspaces',function($query) use($request){
                    $query->where('name','=', $request->search );
                })
                ->orWhereHas('users',function($query) use($request){
                    $query->where('name','LIKE','%'.$request->search.'%');
                })
                ->get();

我得到的结果是错误的,我希望它能够搜索建筑物单位的用户/租户名称。但它也会进入其他建筑物并进行搜索。所以我在结果中让其他建筑物出现了单位。

我认为通过设置 $ building ,可以使所有查询更深入搜索。

$building->units()->with(['users','parkingspaces'])->get();,获取与用户和停车位相连的正确单位。

关于我做错了什么的任何线索?

注意:Auth::user()->current_building()从会话中获取构建ID。

构建模型代码段

class Building extends Model{
  public function users()
  {
    return $this->belongsToMany('App\User','buildings_users');
  }



 public function tenants(){
        return $this->belongsToMany('App\User','buildings_users')->whereHas('roles', function ($query) {
            $query->where('name', '=', 'resident');
        });
  }

  public function staff(){
        return $this->belongsToMany('App\User','buildings_users')->whereHas('roles',function($query){
            $query->where('name','!=','tenant')->where('name','!=','administrator')->where('name','!=','superadministrator');
        });
  }

  public function managers(){
        return $this->belongsToMany('App\User','buildings_users')->whereHas('roles',function($query){
            $query->where('name','=','manager');
        });
  }

  public function units(){
      return $this->hasMany('App\Unit');
  }

单位模型代码段

class Unit extends Model
{
    //
    public function building(){
        return $this->belongsTo('App\Building');
    }

    public function users(){
        return $this->belongsToMany('App\User','user_unit');
    }

    public function parkingspaces(){
        return $this->hasMany('App\Parkingspace');
    }

building_users

CREATE TABLE IF NOT EXISTS `buildings_users` (
  `building_id` int(10) unsigned NOT NULL,
  `user_id` int(10) unsigned NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

user_unit

CREATE TABLE IF NOT EXISTS `user_unit` (
  `building_id` int(10) unsigned NOT NULL,
  `unit_id` int(10) unsigned NOT NULL,
  `user_id` int(10) unsigned NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

单位摘录

CREATE TABLE IF NOT EXISTS `units` (
  `id` int(10) unsigned NOT NULL,
  `building_id` int(10) unsigned NOT NULL,
  `name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB AUTO_INCREMENT=27 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

parkingspaces

CREATE TABLE IF NOT EXISTS `parkingspaces` (
  `id` int(10) unsigned NOT NULL,
  `building_id` int(10) unsigned NOT NULL,
  `unit_id` int(10) unsigned NOT NULL,
  `name` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
  `created_at` timestamp NULL DEFAULT NULL,
  `updated_at` timestamp NULL DEFAULT NULL
) ENGINE=InnoDB AUTO_INCREMENT=108 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

2 个答案:

答案 0 :(得分:1)

问题在于未分组的orWhereHas()电话。

您的$building->units()关系会构建一个类似以下内容的SQL查询:

select * from units
where
    units.building_id = ?
    and units.building_id is not null

然后,您将关系的条件添加到此基本查询中,因此您的查询最终会像:

select * from units
where
    units.building_id = ?
    and units.building_id is not null
    and exists([parkingspaces query])
    or exists([users query])

最后or条件是您获得额外记录的原因。即使所有三个条件都为假,如果最后一个条件匹配,也会返回记录(false AND false AND false OR true === true)。

您需要做的是将您的条件分组,以便or条件适当确定范围(false AND false AND (false OR true) === false)。您可以通过将闭包传递给where()调用来完成此操作:

$units = $building->units()
    ->where(function ($query) use($request){
        return $query
            ->whereHas('parkingspaces',function($query) use($request){
                $query->where('name','=', $request->search );
            })
            ->orWhereHas('users',function($query) use($request){
                $query->where('name','LIKE','%'.$request->search.'%');
            });
    })
    ->get();

这将生成类似以下内容的查询:

select * from units
where
    units.building_id = ?
    and units.building_id is not null
    and (
        exists([parkingspaces query])
        or exists([users query])
    )

现在,您的or条件的范围正确,您只能获得具有匹配ID的建筑物的结果。

答案 1 :(得分:0)

“has-many-through”关系提供了通过中间关系访问远程关系的便捷捷径。

$units = Auth::user()->units;

这样您就可以直接通过用户访问单位:

hasManyThrough

Laravel不会进行查询来访问用户的构建,然后再进行另一个查询来访问该构建的单元,而是使用带有连接的单个查询来获取成员。

{{1}}关系Laravel文档:image repository